package com.fengwk.support.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.fengwk.support.constant.SupportConstant;
import com.fengwk.support.exception.BaseException;
import com.fengwk.support.util.LoggerUtils.Logger;

/*
	@XmlRootElement
	@XmlAccessorType(AccessType.FIELD) 
	@XmlType
	bo类注解注意点：
	@1. 类上的注解  @XmlRootElement 是必不可少的。注解的name属性是可选的，不给节点默认生成为和类名一样，不过全是小写的<student></student>。指定name="studentBean"这生成指定的节点名称,且和指定的大小写一致，有空格的话空格也会出现在标签中。<studentBean></studentBean>
	@2. 成员变量/属性上的注解 @XmlElement 可选的注解。不加或者只是加上@XmlElement,没有指定生成节点的名称，默认是和每个属性的名称一致，就是全变成小写。以age为例,<age></age>,如果给上name属性指定生成节点名称，则生成完全和name属性一致的节点出来，和类上的name属性一样。
	日期类型的要多加一个注解：@XmlJavaTypeAdapter
*/
public class XmlUtils {

	private static final Logger LOG = LoggerUtils.getLogger(XmlUtils.class);
	
	private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	
	private static final boolean DEFAULT_FORMAT = false;
	private static final boolean DEFAULT_CANCEL_HEAD = true;
	
	private static final String SINGLE_QUOTES = "'";// 单引号
	private static final String DOUBLE_QUOTES = "\"";// 双引号
	private static final Pattern PATTERN_PRE_TAG = Pattern.compile("<(?!/).+?>");// 获取前缀标签正则
	
	private XmlUtils() {
		throw new BaseException("can not instance " + getClass());
	}
	
	public static String toXml(Object obj) {
		return toXml(obj, DEFAULT_FORMAT, DEFAULT_CANCEL_HEAD);
	}
	
	public static String toXml(Object obj, boolean format, boolean cancelHead) {
        try {
        	JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_ENCODING, SupportConstant.DEFAULT_CHARSET);
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, format);
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, cancelHead);
			StringWriter sw= new StringWriter();
			marshaller.marshal(obj, sw);
			String xmlStr = sw.toString();
			IoUtils.close(sw);
			return xmlStr;
		} catch (JAXBException e) {
		    LOG.error(e);
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	public static <B> B fromXml(String xml, Class<B> beanClass) {
		Object bean = null;
		try {
			JAXBContext context = JAXBContext.newInstance(beanClass);
	        Unmarshaller unmarshaller = context.createUnmarshaller();
	        Document doc = getDocumentFromXml(xml);
	        if(doc != null) {
	        	bean = unmarshaller.unmarshal(doc);
	        }
		} catch (JAXBException e) {
		    LOG.error(e);
		}
		return (B) bean;
	}
	
	private static Document getDocumentFromXml(String xml) {
		Document document = null;
		InputStream is = IoUtils.toInputStream(xml);
		try {
			DocumentBuilder documentBuilder = factory.newDocumentBuilder();
			document = documentBuilder.parse(is);
		} catch (SAXException | IOException | ParserConfigurationException e) {
			e.printStackTrace();
		} finally {
			IoUtils.close(is);
		}
		return document;
	}
	
	/**
	 * 提取属性内容
	 * 
	 * @param xml
	 * @param attr
	 * @return
	 */
	public static List<String> extractAttr(String xml, String attr) {
		List<String> attrList = new ArrayList<>();
		if (StringUtils.isNotBlank(xml)) {
			xml = xml.replace(SINGLE_QUOTES, DOUBLE_QUOTES);
			Matcher m = PATTERN_PRE_TAG.matcher(xml);
			while (m.find()) {
				String str = m.group();
				int b = str.indexOf(attr);
				if (b == -1)
					continue;
				str = StringUtils.substring(str, b);
				int dm = str.indexOf(DOUBLE_QUOTES);
				str = StringUtils.substring(str, dm + 1);
				dm = str.indexOf(DOUBLE_QUOTES);
				str = StringUtils.substring(str, 0, dm);
				attrList.add(str);
			}
		}
		return attrList;
	}
	
}
